#include "STC89C5xRC.H"
#include "stdint.h"
#include "ds1302.h"
#include "uart.h"
#include "task.h"

#if DS1302_TASK

sbit SCLK = P1 ^ 7;
sbit DATA = P2 ^ 7;
sbit RST = P1 ^ 2;

static const char *TAG = "ds1302.c";

/* define your panel start */
static ds1302_data_t s_ds1302_panel;
/* define your panel end */

/**
 * @brief ds1302_write_read_device
 *
 * @param cmd
 * @param rd_data
 */
static void ds1302_write_read_device(uint8_t cmd, uint8_t *rd_data)
{
    uint8_t data_bit;
    cmd = cmd | 0x01; /* cal cmd is wr_cmd format */
    RST = 0;          /* clear rst */
    SCLK = 0;         /* clear clk */
    RST = 1;          /* set rst */
    _nop_();          /* wait Tcc */

    /* write data to chip start */
    for (data_bit = 0; data_bit < 8; data_bit++)
    {
        DATA = cmd & 0x01; /* write data L_bit->H_bit to chip */
        _nop_();           /* wait Tdc */
        SCLK = 1;          /* set clk */
        _nop_();           /* wait Tcdh */
        SCLK = 0;          /* clear clk */
        cmd >>= 1;         /* move right */
        _nop_();           /* wait Tcl */
    }
    /* write data to chip end */

    /* read data from chip start */
    for (data_bit = 0; data_bit < 8; data_bit++)
    {
        *rd_data >>= 1; /* move right */
        if (DATA == 1)
        {
            *rd_data |= 0x80; /* recv data */
        }
        SCLK = 1; /* set clk */
        _nop_();  /* wait Tcdh */
        SCLK = 0; /* clear clk */
        _nop_();  /* wait Tccz */
    }
    /* read data from chip end */

    SCLK = 0; /* clear clk */
    RST = 0;  /* clear rst */
}

/**
 * @brief ds1302_write_to_device
 *
 * @param cmd
 * @param wr_data
 */
static void ds1302_write_to_device(uint8_t cmd, uint8_t wr_data)
{
    uint8_t data_bit;
    RST = 0;  /* clear rst */
    SCLK = 0; /* clear clk */
    RST = 1;  /* set rst */
    _nop_();  /* wait Tcc */

    /* write data to chip start */
    for (data_bit = 0; data_bit < 8; data_bit++)
    {
        DATA = cmd & 0x01; /* write data L_bit->H_bit to chip */
        _nop_();           /* wait Tdc */
        SCLK = 1;          /* set clk */
        _nop_();           /* wait Tcdh */
        SCLK = 0;          /* clear clk */
        cmd >>= 1;         /* move right */
        _nop_();           /* wait Tcl */
    }
    /* write data to chip end */

    /* write data to chip start */
    for (data_bit = 0; data_bit < 8; data_bit++)
    {
        DATA = wr_data & 0x01; /* write data L_bit->H_bit to chip */
        _nop_();               /* wait Tdc */
        SCLK = 1;              /* set clk */
        _nop_();               /* wait Tcdh */
        SCLK = 0;              /* clear clk */
        wr_data >>= 1;         /* move right */
        _nop_();               /* wait Tcl */
    }
    /* write data to chip end */

    SCLK = 0; /* clear clk */
    RST = 0;  /* clear rst */
}

/**
 * @brief ds1302_read_time
 *
 * @param ds1302_param
 */
static void ds1302_read_time(ds1302_data_t *ds1302_param)
{
    ds1302_write_read_device(SEC_REG, &ds1302_param->sec);                                         /* read sec */
    ds1302_param->sec = ((ds1302_param->sec & 0x70) >> 4) * 10 + (ds1302_param->sec & 0x0F);       /* bcd to dec */
    ds1302_write_read_device(MIN_REG, &ds1302_param->min);                                         /* read min */
    ds1302_param->min = ((ds1302_param->min & 0x70) >> 4) * 10 + (ds1302_param->min & 0x0F);       /* bcd to dec */
    ds1302_write_read_device(HOUR_REG, &ds1302_param->hour);                                       /* read hour */
    ds1302_param->hour = ((ds1302_param->hour & 0x70) >> 4) * 10 + (ds1302_param->hour & 0x0F);    /* bcd to dec */
    ds1302_write_read_device(MDAY_REG, &ds1302_param->mday);                                       /* read mday */
    ds1302_param->mday = ((ds1302_param->mday & 0x70) >> 4) * 10 + (ds1302_param->mday & 0x0F);    /* bcd to dec */
    ds1302_write_read_device(MONTH_REG, &ds1302_param->month);                                     /* read month */
    ds1302_param->month = ((ds1302_param->month & 0x70) >> 4) * 10 + (ds1302_param->month & 0x0F); /* bcd to dec */
    ds1302_write_read_device(YEAR_REG, &ds1302_param->year);                                       /* read year */
    ds1302_param->year = ((ds1302_param->year & 0xf0) >> 4) * 10 + (ds1302_param->year & 0x0F);    /* bcd to dec */
    ds1302_write_read_device(WDAY_REG, &ds1302_param->wday);                                       /* read wday */
    ds1302_param->wday = ds1302_param->wday & 0x07;                                                /* bcd to dec */

#if DS1302_DEBUG
    printf("date: 20%bu-%bu-%bu\r\ntime: %bu:%bu:%bu  wday: %bu\r\n", ds1302_param->year, ds1302_param->month, ds1302_param->mday, ds1302_param->hour, ds1302_param->min, ds1302_param->sec, ds1302_param->wday);
#endif
}

/**
 * @brief ds1302_set_time
 *
 * @param year
 * @param month
 * @param mday
 * @param hour
 * @param min
 * @param sec
 * @param wday
 */
void ds1302_set_time(uint8_t year, uint8_t month, uint8_t mday, uint8_t hour, uint8_t min, uint8_t sec, uint8_t wday)
{
    ds1302_write_to_device(PROTECT_REG, 0x00);                             /* clear protect reg */
    ds1302_write_to_device(SEC_REG, ((sec / 10) << 4 | (sec % 10)));       /* set sec reg */
    ds1302_write_to_device(MIN_REG, ((min / 10) << 4 | (min % 10)));       /* set min reg */
    ds1302_write_to_device(HOUR_REG, ((hour / 10) << 4 | (hour % 10)));    /* set hour reg */
    ds1302_write_to_device(MDAY_REG, ((mday / 10) << 4 | (mday % 10)));    /* set mday reg */
    ds1302_write_to_device(MONTH_REG, ((month / 10) << 4 | (month % 10))); /* set month reg */
    ds1302_write_to_device(YEAR_REG, ((year / 10) << 4 | (year % 10)));    /* set year reg */
    ds1302_write_to_device(WDAY_REG, wday & 0x07);                         /* set wday reg */
    ds1302_write_to_device(0x90, 0xa5);                                    /* clear power reg */
    ds1302_write_to_device(PROTECT_REG, 0x80);                             /* set protect reg */

#if DS1302_DEBUG
    printf("ds1302 set time successfully.\r\n");
#endif
}

/**
 * @brief ds1302_get_time
 *
 * @param data_t
 */
uint8_t ds1302_get_time(ds1302_enum data_t)
{
    switch (data_t)
    {
    case YEAR:
        return s_ds1302_panel.year;
    case MONTH:
        return s_ds1302_panel.month;
    case MDAY:
        return s_ds1302_panel.mday;
    case HOUR:
        return s_ds1302_panel.hour;
    case MIN:
        return s_ds1302_panel.min;
    case SEC:
        return s_ds1302_panel.sec;
    case WDAY:
        return s_ds1302_panel.wday;
    default:
        return SYS_FAIL;
    }
}

/**
 * @brief ds1302_proc
 *
 */
void ds1302_proc(void)
{
    ds1302_read_time(&s_ds1302_panel);
}

#endif